package to.noc.hsm.lunasa.example;
import com.google.common.primitives.Bytes;
import com.safenetinc.luna.LunaUtils;
import com.safenetinc.luna.provider.key.LunaSecretKey;
import org.apache.commons.lang3.SerializationUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.io.Serializable;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import static java.lang.System.out;
/*
* In some cases we may want use Java serialization with encrypted (by the Luna SA HSM)
* host keys. This is a simple example of one way it might be done.
*
* Example output:
*
* HSM KEK (not in the clear):
* 0x0000000000000010
*
* Initial Host Key (in the clear):
* 0x313DDA5BBA4F136B290BDFDCB9B33245311543EC15AD8645
*
* Wrapping IV for Host Key:
* 0x653782F3BA649860
*
* KEK Wrapped Host Key:
* 0x75102B24F1BD0F3DF80D14CA85276A3EC2B82FB1E8E87ACA0756EB5ACA13DBD8
*
* Serialized Key Holder:
* 0xACED000573720047746F2E6E6F632E68736D2E6C756E6173612E6578616D70...
*
* Index of IV in Serialized Data:
* 227
*
* Index of Encrypted Key in Serialized Data:
* 185
*
* Index of Unencrypted Key in Serialized Data:
* -1
*
* Is unwrapped secret key null after deserialization?
* true
*
* Deserialized and Unwrapped Host Key (same as original):
* 0x313DDA5BBA4F136B290BDFDCB9B33245311543EC15AD8645
*
* Plain Text:
* 0xDEADD00D8BADF00DDEADBABED15EA5ED
*
* Cipher Text:
* 0xA2162BBE595A50FE766E894EC0F6BD7F
*
* Plain Text Decrypted:
* 0xDEADD00D8BADF00DDEADBABED15EA5ED
*/
public class WrappedKeySerializationExample {
// Label used to store the the KEK (Key Encryption Key) on the HSM
private static final String KEK_ALIAS = "KEK_3DES";
/*
* Provides a serializable wrapper around the encrypted host key. Key will automatically
* be unwrapped the first time it is used after deserialization.
*/
private static class SerializableKeyHolder implements Serializable {
// Transient objects are not serialized and are null after deserialization
transient private SecretKey secretKey;
private byte[] encryptedHostKey;
private byte[] iv;
private String keyAlgorithm;
private SerializableKeyHolder(String keyAlgorithm, byte[] iv, byte[] encryptedHostKey) {
this.keyAlgorithm = keyAlgorithm;
this.iv = iv;
this.encryptedHostKey = encryptedHostKey;
}
public SecretKey getSecretKey() throws GeneralSecurityException {
if (secretKey == null) {
secretKey = unwrapKeyWithKek(getExistingHsmKek(), keyAlgorithm, iv, encryptedHostKey);
}
return secretKey;
}
}
public static void main(String[] args) throws Exception {
final String hostKeyType = "DESede"; // 3 DES
HsmManager.login();
HsmManager.setSecretKeysExtractable(false);
//
// Create the HSM KEK (Key Encryption Key)
//
SecretKey kek = createNewHsm3desKek();
out.println("HSM KEK (not in the clear):\n\t" + getHex(kek.getEncoded()));
//
// Create a key that's not on the HSM for the purposes of this example
//
SecretKey hostKeyUnencrypted = createSoftwareKey(hostKeyType);
byte[] hostKeyWrappingIv = new byte[8];
new SecureRandom().nextBytes(hostKeyWrappingIv);
out.println("Initial Host Key (in the clear):\n\t " + getHex(hostKeyUnencrypted.getEncoded()));
out.println("Wrapping IV for Host Key:\n\t "+ getHex(hostKeyWrappingIv));
//
// Use the HSM to wrap our host key
//
byte[] wrappedHostKey = wrapKeyWithKek(kek, hostKeyWrappingIv, hostKeyUnencrypted);
out.println("KEK Wrapped Host Key:\n\t " + getHex(wrappedHostKey));
//
// Put the encrypted host key in our serializable holder
//
SerializableKeyHolder keyHolder = new SerializableKeyHolder(hostKeyType, hostKeyWrappingIv, wrappedHostKey);
keyHolder.getSecretKey(); // force key unwrapping before serialization
//
// Serialize our holder into a byte array and destroy our reference to the original object
//
byte[] serializedBytes = SerializationUtils.serialize(keyHolder);
keyHolder = null;
out.println("Serialized Key Holder:\n\t" + getHex(serializedBytes));
//
// Show that the IV and the encrypted key are subarrays of the serialized data, but that
// the unencrypted key is not. It doesn't prove that the unencrypted key isn't there, but
// it's a sanity check nonetheless.
//
int indexOfIvInSerializedData = Bytes.indexOf(serializedBytes, hostKeyWrappingIv);
out.println("Index of IV in Serialized Data:\n\t" + indexOfIvInSerializedData);
int indexOfEncryptedKeyInSerializedData = Bytes.indexOf(serializedBytes, wrappedHostKey);
out.println("Index of Encrypted Key in Serialized Data:\n\t" + indexOfEncryptedKeyInSerializedData);
int indexOfUnencryptedKeyInSerializedData = Bytes.indexOf(serializedBytes, hostKeyUnencrypted.getEncoded());
out.println("Index of Unencrypted Key in Serialized Data:\n\t" + indexOfUnencryptedKeyInSerializedData);
//
// Reconstitute (deserialize) the holder from the byte array
//
keyHolder = SerializationUtils.deserialize(serializedBytes);
out.println("Is unwrapped secret key null after deserialization?\n\t" + (keyHolder.secretKey == null));
out.println("Deserialized and Unwrapped Host Key (same as original):\n\t" +
getHex(keyHolder.getSecretKey().getEncoded()));
//
// Encrypt and decrypt some meaningless data using the HSM just for kicks!
//
byte[] plainText = LunaUtils.hexStringToByteArray("DEADD00D8BADF00DDEADBABED15EA5ED");
out.println("Plain Text:\n\t" + getHex(plainText));
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding", "LunaProvider");
SecretKey hostKey = keyHolder.getSecretKey();
cipher.init(Cipher.ENCRYPT_MODE, hostKey);
byte[] cipherText = cipher.doFinal(plainText);
out.println("Cipher Text:\n\t" + getHex(cipherText));
cipher.init(Cipher.DECRYPT_MODE, hostKey);
byte[] plainTextDecrypted = cipher.doFinal(cipherText);
out.println("Plain Text Decrypted:\n\t" + getHex(plainTextDecrypted));
HsmManager.logout();
}
// KEK is generated on HSM. We never know its value, just it's alias name.
private static SecretKey createNewHsm3desKek() throws GeneralSecurityException {
if (HsmManager.hasSavedKey(KEK_ALIAS)) {
HsmManager.deleteKey(KEK_ALIAS);
}
KeyGenerator kg = KeyGenerator.getInstance("DESede", "LunaProvider");
kg.init(256);
LunaSecretKey kek = (LunaSecretKey) kg.generateKey();
HsmManager.saveKey(KEK_ALIAS, kek);
return kek;
}
private static SecretKey getExistingHsmKek() throws GeneralSecurityException {
// casting KEY -> SecretKey
return (SecretKey) HsmManager.getSavedKey(KEK_ALIAS);
}
// create software-only (not stored on HSM) key
private static SecretKey createSoftwareKey(String keyType) throws NoSuchAlgorithmException {
KeyGenerator generator = KeyGenerator.getInstance(keyType);
generator.init(new SecureRandom());
return generator.generateKey();
}
// wrapping operation is performed on the HSM
private static byte[] wrapKeyWithKek(SecretKey hsmKek, byte[] wrappingIv, SecretKey keyToBeWrapped) throws GeneralSecurityException {
Cipher wrappingCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding", "LunaProvider");
AlgorithmParameters algParams = AlgorithmParameters.getInstance("IV", "LunaProvider");
algParams.init(new IvParameterSpec(wrappingIv));
wrappingCipher.init(Cipher.WRAP_MODE, hsmKek, algParams);
return wrappingCipher.wrap(keyToBeWrapped);
}
private static SecretKey unwrapKeyWithKek(SecretKey hsmKek, String wrappedKeyAlgo, byte[] wrappedKeyIv, byte[] wrappedKeyBytes) throws GeneralSecurityException {
Cipher wrappingCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding", "LunaProvider");
AlgorithmParameters algParams = AlgorithmParameters.getInstance("IV", "LunaProvider");
algParams.init(new IvParameterSpec(wrappedKeyIv));
wrappingCipher.init(Cipher.UNWRAP_MODE, hsmKek, algParams);
return (SecretKey) wrappingCipher.unwrap(wrappedKeyBytes, wrappedKeyAlgo, Cipher.SECRET_KEY);
}
private static String getHex(byte array[]) {
return "0x" + LunaUtils.getHexString(array, false).toUpperCase();
}
}